<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<![endif]-->
<title>VIM 中文帮助: 作业、通道、进程间通信</title>
<link rel="stylesheet" href="vim-stylesheet.css" type="text/css" />
<link rel="canonical" href="https://yianwillis.github.io/vimcdoc/doc/channel.html" />
<script type="text/javascript" src="vimcdoc.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<nav id=banner>
<form action=tags.html target="tag_iframe">
  <input type="text" name="tag" id="tag" placeholder="标签搜索">
</form>
<iframe name="tag_iframe" src=""></iframe>
<a href="help.html">帮助总览</a> &middot;
<hr/>
<a href="quickref.html">快速参考</a> &middot;
<a href="index.html">命令索引</a> &middot;
<a href="eval.html#functions">函数列表</a> &middot;
<a href="quickref.html#option-list">选项列表</a> &middot;
<hr/>
<a href="usr_toc.html">用户手册</a> &middot;
<a href="help.html#reference_toc">参考手册</a> &middot;
</nav>

<header>
<h2>channel</h2>
</header>
<article id=outer>
<section class=inner>
<b class="vimtag"> <a name="channel.txt">channel.txt</a> </b>   适用于 Vim 9.0 版本。   最近更新: 2023年2月


                  <code class="vim">VIM 参考手册    by Bram Moolenaar</code>
                                <code class="vim">译者</code>: Willis


                      进 程 间 通 信                            <b class="vimtag"> <a name="channel">channel</a> </b>

Vim 使用通道 (channel) 和其他进程通信。
通道使用套接字 (socket) 或管道 pipe 机制。              <b class="vimtag"> <a name="socket-interface">socket-interface</a> </b>
作业 (job) 用来启动进程并与之通信。
Netbeans 接口也使用通道。 <a href="netbeans.html#netbeans">netbeans</a> 

1. 总览                                  <a href="channel.html#job-channel-overview">job-channel-overview</a> 
2. 通道演示                              <a href="channel.html#channel-demo">channel-demo</a> 
3. 打开通道                              <a href="channel.html#channel-open">channel-open</a> 
4. 使用 JSON 或 JS 通道                  <a href="channel.html#channel-use">channel-use</a> 
5. 通道命令                              <a href="channel.html#channel-commands">channel-commands</a> 
6. 使用 RAW 或 NL 通道                   <a href="channel.html#channel-raw">channel-raw</a> 
7. 更多通道函数                          <a href="channel.html#channel-more">channel-more</a> 
8. 通道函数详情                          <a href="channel.html#channel-functions-details">channel-functions-details</a> 
9. 带通道启动作业                        <a href="channel.html#job-start">job-start</a> 
10. 不带通道启动作业                     <a href="channel.html#job-start-nochannel">job-start-nochannel</a> 
11. 作业函数                             <a href="channel.html#job-functions-details">job-functions-details</a> 
12. 作业选项                             <a href="channel.html#job-options">job-options</a> 
13. 控制作业                             <a href="channel.html#job-control">job-control</a> 
14. 使用提示缓冲区                       <a href="channel.html#prompt-buffer">prompt-buffer</a> 
15. 语言服务器协议                       <a href="channel.html#language-server-protocol">language-server-protocol</a> 

                                                        <b class="vimtag"> <a name="E1277">E1277</a> </b>
<code class="notvi">{仅当编译时带有  <a href="various.html#+channel">+channel</a>  特性时才有通道功能}</code>
        可以这样查看: `has(<code class="badlink">'channel'</code>)`
<code class="notvi">{仅当编译时带有  <a href="various.html#+job">+job</a>  特性时才有作业功能}</code>
        可以这样查看: `has(<code class="badlink">'job'</code>)`

</section><hr class="doubleline" /><section class=inner>
<h4>1. 总览                                         <b class="vimtag"> <a name="job-channel-overview">job-channel-overview</a> </b></h4>
有四种主要的作业类型:
1. 守护进程 (daemon)，服务于多个 Vim 实例。
   Vim 使用套接字连接。
2. 用于单个 Vim 实例的单个作业，异步操作。
   使用套接字或管道。
3. 用于短期若干工作的作业，异步操作。
   使用套接字或管道。
4. 作为过滤器，同步操作。
   使用管道。

使用套接字请见  <a href="channel.html#job-start">job-start</a> 、 <a href="channel.html#job-start-nochannel">job-start-nochannel</a>  和  <a href="channel.html#channel-open">channel-open</a> 。2 和 3 类
中一个或多个作业若使用管道，可见  <a href="channel.html#job-start">job-start</a> 。4 类即 ":<code class="special">{range}</code>!cmd" 命令，参见
 <a href="change.html#filter">filter</a> 。

套接字和管道可用以下协议:
RAW     无规范，Vim 不知信息如何终止
NL      每条信息由 NL (换行) 字符终止
JSON    JSON 编码  <a href="builtin.html#json_encode()">json_encode()</a> 
JS      JavaScript 风格类 JSON 的编码  <a href="builtin.html#js_encode()">js_encode()</a> 
LSP     语言服务器协议编码  <a href="channel.html#language-server-protocol">language-server-protocol</a> 

常用组合有:
- 使用 NL 模式的管道连接的作业。例如，运行风格检查器并接收错误与<code class="note">警告</code>。
- 使用 JSON 模式的套接字连接的守护进程。例如，从数据库中查找交叉索引。

</section><hr class="doubleline" /><section class=inner>
<h4>2. 通道演示                             <b class="vimtag"> <a name="channel-demo">channel-demo</a> </b> <b class="vimtag"> <a name="demoserver.py">demoserver.py</a> </b></h4>
需要 Python 支持。演示程序可见
$VIMRUNTIME/tools/demoserver.py
在终端上运行上。让我们称之为 T1。

在另一个终端上运行 Vim。连接到演示服务器: 
<code class="example">        let channel = ch_open('localhost:8765')</code>
<code class="example"></code>
T1 中可见:
<code class="section">        === socket opened === </code>

现在可以给服务器发消息: 
<code class="example">        echo ch_evalexpr(channel, 'hello!')</code>
<code class="example"></code>
T1 接收到消息，并发送响应给 Vim。
T1 中可见原始的消息流。Vim 发送的是:
<code class="section">        [1,"hello!"] </code>
响应则是:
<code class="section">        [1,"got it"] </code>
每次发送一条新消息时，该数值会递增。

服务器可以给 Vim 发送命令。在 T1 上输入 (按文本，包括引号):
<code class="section">        ["ex","echo 'hi there'"] </code>
此时 Vim 中可以看到该消息。要移动光标前进一个单词:
<code class="section">        ["normal","w"] </code>

要处理异步通信，需要提供回调函数: 
<code class="example">        func MyHandler(channel, msg)</code>
<code class="example">          echo "from the handler: " .. a:msg</code>
<code class="example">        endfunc</code>
<code class="example">        call ch_sendexpr(channel, 'hello!', {'callback': "MyHandler"})</code>
Vim 不会等待响应。这样，服务器就可以稍后发送响应，响应时会调用 MyHandler。

回调除了每次在发送时以外，也可以在打开通道时提供: 
<code class="example">        call ch_close(channel)</code>
<code class="example">        let channel = ch_open('localhost:8765', {'callback': "MyHandler"})</code>
<code class="example">        call ch_sendexpr(channel, 'hello!')</code>
<code class="example"></code>
测试通道时可能希望能知道发生了什么。可以告诉 Vim 在日志文件里记录事件: 
<code class="example">        call ch_logfile('channellog', 'w')</code>
见  <a href="channel.html#ch_logfile()">ch_logfile()</a> 。

</section><hr class="doubleline" /><section class=inner>
<h4>3. 打开通道                                             <b class="vimtag"> <a name="channel-open">channel-open</a> </b></h4>
要打开通道: 
<code class="example">    let channel = ch_open({address} [, {options}])</code>
<code class="example">    if ch_status(channel) == "open"</code>
<code class="example">      " use the channel</code>
<code class="example"></code>
可用  <a href="channel.html#ch_status()">ch_status()</a>  看到通道是否已打开。

                                        <b class="vimtag"> <a name="channel-address">channel-address</a> </b>
<code class="special">{address}</code> 可以是域名或 IP 地址后跟端口号，也可以是带 "unix:" 前缀的 Unix-域套
接字路径。例如 
<code class="example">    www.example.com:80   " 域名 + port</code>
<code class="example">    127.0.0.1:1234       " IPv4 + port</code>
<code class="example">    [2001:db8::1]:8765   " IPv6 + port</code>
<code class="example">    unix:/tmp/my-socket  " Unix-域套接字路径</code>
<code class="example"></code>
<code class="special">{options}</code> 是可选项目的字典:                             <b class="vimtag"> <a name="channel-open-options">channel-open-options</a> </b>

"mode" 可以是:                                          <b class="vimtag"> <a name="channel-mode">channel-mode</a> </b>
        "json" - JSON，见下；最方便的方式。缺省。
        "js"   - JS (JavaScript) 编码，比 JSON 经济。
        "nl"   - Nl 字符结尾的消息
        "raw"  - 原始消息
        "lsp"  - 语言服务器协议编码
                                                <b class="vimtag"> <a name="channel-callback">channel-callback</a> </b> <b class="vimtag"> <a name="E921">E921</a> </b>
"callback"      消息收到时，如果还未被处理 (如 ID 为零的 JSON 消息)，调用本函
                数。接受两个参数: 通道和收到的消息。例如: 
<code class="example">        func Handle(channel, msg)</code>
<code class="example">          echo '收到: ' .. a:msg</code>
<code class="example">        endfunc</code>
<code class="example">        let channel = ch_open("localhost:8765", {"callback": "Handle"})</code>

                "mode" 如果是 "json"、"js" 或 "lsp"，"msg" 参数是转换到 Vim 类
                型的收到消息的正文。
                "mode" 如果是 "nl"，"msg" 参数是单个消息，不包含 NL。
                "mode" 如果是 "raw"，"msg" 参数是作为字符串的整个消息。

                对所有的回调: 可用  <a href="builtin.html#function()">function()</a>  绑定它到参数和/或字典。也可用
                "dict.function" 形式来绑定到字典。

                只有在 "安全" 时才会调用回调函数，通常是 Vim 等待用户输入字符
                的时候。Vim 不支持多线程。

                                                        <b class="vimtag"> <a name="close_cb">close_cb</a> </b>
"close_cb"      通道关闭时，除非调用了 ch_close()，调用本函数。定义如下: 
<code class="example">        func MyCloseHandler(channel)</code>
                Vim 会调用回调函数先处理完所有数据，然后再调用 close_cb。当此
                函数调用时，不会再有更多数据传到回调函数。不过，如果回调在让
                Vim 检查消息，close_cb 调用时可能还在回调处理中。插件必须以某
                种方式能处理这种情况，不过知道没有更多数据会传来还是有用的。
                如果不确定有无消息可读，可用 try/catch 块: 
<code class="example">                        try</code>
<code class="example">                          let msg = ch_readraw(a:channel)</code>
<code class="example">                        catch</code>
<code class="example">                          let msg = 'no message'</code>
<code class="example">                        endtry</code>
<code class="example">                        try</code>
<code class="example">                          let err = ch_readraw(a:channel, #{part: 'err'})</code>
<code class="example">                        catch</code>
<code class="example">                          let err = 'no error'</code>
<code class="example">                        endtry</code>
                                                        <b class="vimtag"> <a name="channel-drop">channel-drop</a> </b>
"drop"          指定如何丢弃消息:
                    "auto"      没有回调来处理消息时。"close_cb" 也考虑在内。
                    "never"     所有消息都会保存。

                                                        <b class="vimtag"> <a name="channel-noblock">channel-noblock</a> </b>
"noblock"       和  <a href="channel.html#job-noblock">job-noblock</a>  效果相同。只适用于写操作。

                                                        <b class="vimtag"> <a name="waittime">waittime</a> </b>
"waittime"      等待连接完成的时间，以毫秒计。负数代表永久等待。

                缺省为零，不等待。用于本地服务器已经运行的情形。Unix 上 Vim 实
                际使用 1 毫秒等待时间，在很多系统上这是必要的。对于远程服务服
                务器，使定更高的值，例如，至少 10 毫秒。
                                                        <b class="vimtag"> <a name="channel-timeout">channel-timeout</a> </b>
"timeout"       等待请求处理完成的阻塞时间，例如 ch_evalexpr()。以毫秒计。缺省
                为 2000 (2 秒)。

"mode" 为 "json" 或 "js" 时，"callback" 是可选的。省略时只有在发送一条消息后才
能收到消息。

打开通道后要改变通道选项，用  <a href="channel.html#ch_setoptions()">ch_setoptions()</a> 。参数类似于  <a href="channel.html#ch_open()">ch_open()</a>  所用的
参数，但不能指定 "waittime"，因为它只在通道打开时有意义。

例如，可以新增或改变处理函数: 
<code class="example">    call ch_setoptions(channel, {'callback': callback})</code>
"callback" 为空 (零或空字符串) 时删除该处理函数。

回调执行后 Vim 会刷新屏幕，并复原光标之前所在的位置。因而，回调无须执行
 <a href="various.html#:redraw">:redraw</a> 。

可以改变 timeout: 
<code class="example">    call ch_setoptions(channel, {'timeout': msec})</code>

                                                          <b class="vimtag"> <a name="channel-close">channel-close</a> </b> <b class="vimtag"> <a name="E906">E906</a> </b>
用完通道后，这样可以关闭之: 
<code class="example">    call ch_close(channel)</code>
使用套接字时，关闭双向的套接字。使用管道时 (stdin/stdout/stderr)，关闭所有的管
道。这可能不是你想要的！用 job_stop() 停止作业可能更好。
放弃所有预读取，不再调用回调。

<code class="note">注意</code> 通道关闭分三个阶段:
  - I/O 结束，记录日志: "Closing channel"。队列中可能还有消息等待读取或回调。
  - 清理预读取，记录日志: "Clearing channel"。有些变量还可能引用该通道。
  - 释放通道，记录日志: "Freeing channel"。

通道如果不能打开，会报错。MS-Windows 和 Unix 有一点区别。Unix 上如果打不开端
口，会迅速失败。MS-Windows 上会等上 "waittime"。
<b class="vimtag"> <a name="E898">E898</a> </b> <b class="vimtag"> <a name="E901">E901</a> </b> <b class="vimtag"> <a name="E902">E902</a> </b>

读写通道时如果有错，会关闭通道。
<b class="vimtag"> <a name="E630">E630</a> </b> <b class="vimtag"> <a name="E631">E631</a> </b>

</section><hr class="doubleline" /><section class=inner>
<h4>4. 使用 JSON 或 JS 通道                                         <b class="vimtag"> <a name="channel-use">channel-use</a> </b></h4>
JSON 模式可以以同步方式发送消息: 
<code class="example">    let response = ch_evalexpr(channel, {expr})</code>
此时，等待对方回复响应。

JS 模式也是如此，但消息使用 JavasScript 编码。其中的区别，见  <a href="builtin.html#js_encode()">js_encode()</a> 。

要发送消息，但不立即处理响应或让通道通过回调来处理响应: 
<code class="example">    call ch_sendexpr(channel, {expr})</code>
<code class="example"></code>
要发送消息并异步通过指定函数来处理响应: 
<code class="example">    call ch_sendexpr(channel, {expr}, {'callback': Handler})</code>
<code class="example"></code>
Vim 会通过消息 ID 来匹配请求和响应。一旦收到消息，会调用回调。相同 ID 的后来的
响应会被忽略。如果你的服务器返回多个响应，必须用 ID 为零进行发送，它们都会被通
道回调收到。

<code class="special">{expr}</code> 转换为 JSON 格式，以数据形式出现。例如，<code class="special">{expr}</code> 如果是字符串 "hello"，接
收方可能会收到消息:
<code class="section">        [12,"hello"] </code>

JSON 发送的格式是:
    [<code class="special">{number}</code>,<code class="special">{expr}</code>]

<code class="special">{number}</code> 每次都不同。对应的响应 (如果有的话) 必须使用同样的数值:

    [<code class="special">{number}</code>,<code class="special">{response}</code>]

这样 Vim 就会知道哪个发送的消息对应哪个收到的消息，并调用正确的处理函数。即使
消息接收的顺序打乱也不要紧。

JSON 文本以换行符结尾。可用于分隔读取的文本。例如，Python 中:
        splitidx = read_text.find('\n')
        message = read_text[:splitidx]
        rest = read_text[splitidx + 1:]

发送者必须发送合法的 JSON 给 Vim。Vim 会分析 JSON 来检查消息是否完整结尾。只
有完整结尾的消息才会被接受。消息结尾的换行符是可选的。

一个进程如果没有先收到消息，而要主动发送消息给 Vim 时，应使用数值零:
    [0,<code class="special">{response}</code>]

此时通道处理函数会把 <code class="special">{response}</code> 转换为 Vim 类型。如果通道没有处理函数，该消息
被丢弃。

JSON 或 JS 通道也可以用 ch_sendraw() 和 ch_evalraw()。调用者必须自行负责正确的
编码和解码。

</section><hr class="doubleline" /><section class=inner>
<h4>5. 通道命令                                             <b class="vimtag"> <a name="channel-commands">channel-commands</a> </b></h4>
通过 JSON 通道，进程可以向 Vim 发送一些 Vim 内部可以处理，无需通道处理函数的命
令。

可能命令包括:                                   <b class="vimtag"> <a name="E903">E903</a> </b> <b class="vimtag"> <a name="E904">E904</a> </b> <b class="vimtag"> <a name="E905">E905</a> </b>
    ["redraw", <code class="special">{forced}</code>]
    ["ex",     {Ex 命令}]
    ["normal", <code class="special">{普通模式命令}</code>]
    ["expr",   <code class="special">{expression}</code>, <code class="special">{number}</code>]
    ["expr",   <code class="special">{expression}</code>]
    ["call",   <code class="special">{函数名}</code>, <code class="special">{参数列表}</code>, <code class="special">{number}</code>]
    ["call",   <code class="special">{函数名}</code>, <code class="special">{参数列表}</code>]

适用于所有以上命令: 小心命令的后果！很容易干扰用户正在进行的操作。要避免麻烦，
通过  <a href="builtin.html#mode()">mode()</a>  检查编辑器是否在你期待的状态下。例如，要发送键序列作为文本插入，
而不是作为命令执行:
<code class="section">    ["ex","if mode() == 'i' | call feedkeys('ClassName') | endif"] </code>

这些命令产生的错误通常不向用户报告，以免弄乱用户的显示。如果你确实想看到，把
<a href="options.html#'verbose'">'verbose'</a> 选项设为 3 或更高。


<code class="section">"redraw" 命令</code>

其它命令特意不刷新屏幕，以便你发送若干命令而不会移动光标。有些命令的副作用仍可
能导致重画。要显示改变的文本并移动光标到其恰当的位置，必须用 'redraw" 命令结
束。

参数通常是空字符串:
<code class="section">        ["redraw", ""] </code>
要先清屏，传入 "force":
<code class="section">        ["redraw", "force"] </code>


<code class="section">"ex" 命令 </code>

"ex" 命令可用以执行任意 Ex 命令。没有完成或报错的任何响应。可以调用  <a href="userfunc.html#autoload">autoload</a> 
脚本内的函数:
<code class="section">        ["ex","call myscript#MyFunc(arg)"] </code>

也可以用 "call  <a href="builtin.html#feedkeys()">feedkeys()</a> " 插入任何键序列。

如果有错，通道日志 (如果设置的话) 会有记录。v:errmsg 设为错误信息。


<code class="section">"normal" 命令 </code>

"normal" 命令的执行如同 ":normal!"，命令不被映射。例如，要打开光标下的折叠:
<code class="section">        ["normal" "zO"] </code>


<code class="section">带响应的 "expr" 命令 </code>

"expr" 命令可用于计算表达式求值。例如，要得到当然缓冲区的行数:
<code class="section">        ["expr","line('$')", -2] </code>

它会把表达式计算的结果送回:
<code class="section">        [-2, "last line"] </code>
格式是:
        [<code class="special">{number}</code>, <code class="special">{result}</code>]

<code class="special">{number}</code> 和请求中的数值对应。使用负数以避免和 Vim 发送的信息有冲突。每个请求使
用不同的数值有助于把响应的请求对应起来。

<code class="special">{result}</code> 是计算的结果，以 JSON 编码。如果计算失败，或者结果不能以 JSON 编码，
则返回字符串 "ERROR"。


<code class="section">不带响应的 "expr" 命令 </code>

和上面的 "expr" 命令类似，但不需要返回任何响应。例如:
<code class="section">        ["expr","setline('$', [<code class="badlink">'one'</code>, <code class="badlink">'two'</code>, <code class="badlink">'three'</code>])"] </code>
请求中没有第三个参数。


<code class="section">"call" 命令 </code>

和 "expr" 类似，但不是把整个表达式作为一个字符串传递，而是分别传递函数名和一个
参数列表。这样有助于避免把参数经过转义、连接等步骤转换为字符串的过程。例如:
<code class="section">        ["call", "line", ["$"], -2] </code>

如果不需要响应，不用提供第四个参数:
<code class="section">        ["call", "setline", ["$", ["one", "two", "three"]]] </code>

</section><hr class="doubleline" /><section class=inner>
<h4>6. 使用 RAW 或 NL 通道                                  <b class="vimtag"> <a name="channel-raw">channel-raw</a> </b></h4>
使用 RAW 或 NL 模式时，可以这样发送信息: 
<code class="example">    let response = ch_evalraw(channel, {string})</code>
<code class="example"></code>
<code class="special">{string}</code> 直接传送。响应也是通道读取的直接结果。既然 Vim 不知道如何识别信息怎样
结尾，你需要自己处理。超时只及于首个字节的读取，之后将不再等待。

模式为 "nl" 时，发送信息的方式也类似。每个消息之后你应该加上 NL。这样你可以一
次发送多个 NL 结尾的信息。响应为到首个 NL (包含) 为止的文本。也包括只有 NL 的
空响应。如果通道超时还没有收到 NL，返回空字符串。

要发送信息并不等待响应: 
<code class="example">    call ch_sendraw(channel, {string})</code>
进程会发返响应，会调用通道处理函数。

                                                <b class="vimtag"> <a name="channel-onetime-callback">channel-onetime-callback</a> </b>
可发送信息并使响应异步地被指定函数处理: 
<code class="example">    call ch_sendraw(channel, {string}, {'callback': 'MyHandler'})</code>
<code class="example"></code>
<code class="special">{string}</code> 也可以是 JSON，用  <a href="builtin.html#json_encode()">json_encode()</a>  可创建， <a href="builtin.html#json_decode()">json_decode()</a>  可用来处理
收到的 JSON 信息。

原始通道不可使用  <a href="channel.html#ch_evalexpr()">ch_evalexpr()</a>  或  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a> 。

Vim 的字符串不可含有 NUL 字符。要收发 NUL 字符，请见  <a href="channel.html#in_io-buffer">in_io-buffer</a>  和
 <a href="channel.html#out_io-buffer">out_io-buffer</a> 。

</section><hr class="doubleline" /><section class=inner>
<h4>7. 更多通道函数                                         <b class="vimtag"> <a name="channel-more">channel-more</a> </b></h4>
要获知通道的状态: ch_status(channel)。可能的值为:
        "fail"          通道打开失败。
        "open"          通道可用。
        "buffered"      通道已关闭，但还有待读的数据。
        "closed"        通道已关闭。

要获知通道相关的作业: ch_getjob(channel)

要从通道读取一条信息: 
<code class="example">        let output = ch_read(channel)</code>
这里使用通道的 timeout。要读取不带超时以获得所有所用的信息: 
<code class="example">        let output = ch_read(channel, {'timeout': 0})</code>
如果无信息可读，结果在 JSON 或 JS 模式通道下为 v:none，RAW 或 NL 通道下为空字
符串。可以用  <a href="channel.html#ch_canread()">ch_canread()</a>  检查是否有信息可读。

<code class="note">注意</code> 如果没有回调，消息会被丢弃。要避免之，给通道加入一个关闭回调函数。

要读取 RAW 通道所有可用的正常输出: 
<code class="example">        let output = ch_readraw(channel)</code>
要读取 RAW 通道所有可用的错误输出: 
<code class="example">        let output = ch_readraw(channel, {"part": "err"})</code>
<code class="note">注意</code> 通道使用 NL 模式时，ch_readraw() 每次调用只返回一行。

ch_read() 和 ch_readraw() 都适用通道 timeout。如果在该时间内没有任何消息可读，
返回空字符串。要指定其他的超时，使用以毫秒计的 "timeout" 选项:
<code class="section">        {"timeout": 123} </code>
要读取错误输出，使用 "part" 选项:
<code class="section">        {"part": "err"} </code>
要在 JS 或 JSON 通道上读取指定 ID 的信息:
<code class="section">        {"id": 99} </code>
如果没有给出 ID 或 ID 为 -1，返回首个信息。这里的优先级高于等待该信息的回调。

RAW 通道这里返回任何可用的信息，因为 Vim 不知道信息如何终止。
NL 通道这里返回一条信息。
JS 或 JSON 通道这里返回一条解码的信息。
包含任何序列号。

</section><hr class="doubleline" /><section class=inner>
<h4>8. 通道函数详情                                 <b class="vimtag"> <a name="channel-functions-details">channel-functions-details</a> </b></h4>
ch_canread(<code class="special">{handle}</code>)                                            <b class="vimtag"> <a name="ch_canread()">ch_canread()</a> </b>
                如果从 <code class="special">{handle}</code> 有内容可读，返回非零。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。

                用于在合适的时间才从通道读取的场合，例如在定时器中。

                <code class="note">注意</code> 如果通道没有回调，这些消息被丢弃。要避免这一点，加上关闭
                回调函数。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_canread()</code>
<code class="example"></code>
ch_close(<code class="special">{handle}</code>)                                              <b class="vimtag"> <a name="ch_close()">ch_close()</a> </b>
                关闭 <code class="special">{handle}</code>。见  <a href="channel.html#channel-close">channel-close</a> 。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                不调用关闭回调函数。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_close()</code>
<code class="example"></code>
ch_close_in(<code class="special">{handle}</code>)                                           <b class="vimtag"> <a name="ch_close_in()">ch_close_in()</a> </b>
                只关闭 <code class="special">{handle}</code> 的 "in" 部分。见  <a href="channel.html#channel-close-in">channel-close-in</a> 。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                不调用关闭回调函数。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_close_in()</code>
<code class="example"></code>
<code class="example"></code>
ch_evalexpr(<code class="special">{handle}</code>, <code class="special">{expr}</code> [, <code class="special">{options}</code>])                     <b class="vimtag"> <a name="ch_evalexpr()">ch_evalexpr()</a> </b>
                发送 <code class="special">{expr}</code> 到 <code class="special">{handle}</code> 上。<code class="special">{expr}</code> 用通道类型进行编码。不能于
                原始通道。见  <a href="channel.html#channel-use">channel-use</a> 。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                使用 "lsp" 通道模式时，<code class="special">{expr}</code> 必须为  <a href="eval.html#Dict">Dict</a> 。
                                                                <b class="vimtag"> <a name="E917">E917</a> </b>
                <code class="special">{options}</code> 必须是字典。不能有 "callback" 项目。可以有 "timeout"
                项目来指明此特定请求的超时。

                ch_evalexpr() 等待响应并返回经解码的表达式。如果有错或超时，
                返回空  <a href="eval.html#String">String</a> ，或者用 "lsp" 通道模式时，返回空  <a href="eval.html#Dict">Dict</a> 。

                <code class="note">注意</code> 在等待响应时，Vim 在处理其它的信息。要确保这不会有问题。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_evalexpr(expr)</code>
<code class="example"></code>
<code class="example"></code>
ch_evalraw(<code class="special">{handle}</code>, <code class="special">{string}</code> [, <code class="special">{options}</code>])            <b class="vimtag"> <a name="ch_evalraw()">ch_evalraw()</a> </b>
                发送 <code class="special">{expr}</code> 到 <code class="special">{handle}</code> 上。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。

                类似于  <a href="channel.html#ch_evalexpr()">ch_evalexpr()</a> ，但不对请求编码也不对响应结果解码。调用
                者负责内容的正确性。也不对 NL 模式的通道附加换行符，调用者须自
                行负责。响应中的 NL 则被清除。
                <code class="note">注意</code> Vim 不能判定原始通道上的文本何时被完整地收到。它只返回第
                一部分，你需用  <a href="channel.html#ch_readraw()">ch_readraw()</a>  来读取其余的部分。
                见  <a href="channel.html#channel-use">channel-use</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_evalraw(rawstring)</code>
<code class="example"></code>
ch_getbufnr(<code class="special">{handle}</code>, <code class="special">{what}</code>)                            <b class="vimtag"> <a name="ch_getbufnr()">ch_getbufnr()</a> </b>
                返回 <code class="special">{handle}</code> 用于字符串 <code class="special">{what}</code> 的缓冲区号。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                <code class="special">{what}</code> 可以是 "err"，代表 stderr，"out" 代表 stdout，或空，代
                表套接字输出。
                如果没有这样的缓冲区，返回 -1。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_getbufnr(what)</code>
<code class="example"></code>
ch_getjob(<code class="special">{channel}</code>)                                            <b class="vimtag"> <a name="ch_getjob()">ch_getjob()</a> </b>
                取得 <code class="special">{channel}</code> 相关的作业。
                如果没有这样的作业，在返回的作业上调用  <a href="channel.html#job_status()">job_status()</a>  会返回
                "fail"。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_getjob()</code>
<code class="example"></code>
<code class="example"></code>
ch_info(<code class="special">{handle}</code>)                                               <b class="vimtag"> <a name="ch_info()">ch_info()</a> </b>
                返回 <code class="special">{handle}</code> 相关信息的一个字典。其项目为:
                   "id"           通道号
                   "status"       "open"、"buffered" 或 "closed"，可见
                                  ch_status()
                用于 ch_open() 打开的通道时:
                   "hostname"     地址的机器名
                   "port"         地址的端口号
                   "path"         Unix-域套接字的路径
                   "sock_status"  "open" 或 "closed"
                   "sock_mode"    "NL"、"RAW"、"JSON" 或 "JS"
                   "sock_io"      "socket"
                   "sock_timeout" 毫秒计的超时

                <code class="note">注意</code> "path" 只在使用 Unix-域套接字时出现，常规地址只会出现
                "hostname" 和 "port"。

                用于 job_start() 打开的通道时:
                   "out_status"   "open"、"buffered" 或 "closed"
                   "out_mode"     "NL"、"RAW"、"JSON" 或 "JS"
                   "out_io"       "null"、"pipe"、"file" 或 "buffer"
                   "out_timeout"  毫秒计的超时
                   "err_status"   "open"、"buffered" 或 "closed"
                   "err_mode"     "NL"、"RAW"、"JSON" 或 "JS"
                   "err_io"       "out"、"null"、"pipe"、"file" 或 "buffer"
                   "err_timeout"  毫秒计的超时
                   "in_status"    "open" 或 "closed"
                   "in_mode"      "NL"、"RAW"、"JSON"、"JS" 或 "LSP"
                   "in_io"        "null"、"pipe"、"file" 或 "buffer"
                   "in_timeout"   毫秒计的超时

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_info()</code>
<code class="example"></code>
<code class="example"></code>
ch_log(<code class="special">{msg}</code> [, <code class="special">{handle}</code>])                                      <b class="vimtag"> <a name="ch_log()">ch_log()</a> </b>
                如果用  <a href="channel.html#ch_logfile()">ch_logfile()</a>  打开日志，把字符串 <code class="special">{msg}</code> 写入通道日志文
                件中。
                在消息之前附加文本 "ch_log():" ，以明确消息是来自此函数调用，
                方便在日志文件中查找。
                如果传入 <code class="special">{handle}</code>，使用该通道号写入信息。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。所用的通道号对应的通道必
                须已打开。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        'did something'-&gt;ch_log()</code>
<code class="example"></code>
<code class="example"></code>
ch_logfile(<code class="special">{fname}</code> [, <code class="special">{mode}</code>])                                  <b class="vimtag"> <a name="ch_logfile()">ch_logfile()</a> </b>
                启动通道活动记录，写入 <code class="special">{fname}</code>。
                如果 <code class="special">{fname}</code> 为空字符串，停止记录。

                如果 <code class="special">{mode}</code> 省略或包含 "a" 或为 "o"，附加到文件。
                如果 <code class="special">{mode}</code> 包含 "w" 而不包含 "a"，用新文件开始。
                如果 <code class="special">{mode}</code> 包含 "o"，记录所有终端输出。否则，只记录部分有意义
                的终端输出。

                用  <a href="channel.html#ch_log()">ch_log()</a>  来写入日志信息。每条消息后都会刷新文件，Unix 上
                可用 "tail -f" 实时监控。

                要尽早打开日志以看到启动时终端接收的内容，可用  <a href="starting.html#--log">--log</a>  (此时使
                用 "ao" 模式): 
<code class="example">                        vim --log logfile</code>

                此函数在  <a href="eval.html#sandbox">sandbox</a>  中不可用。
                <code class="note">注意</code>: 通道在文件中保存通信内容，小心其中可能包含机密和敏感的隐
                私信息，如你在终端窗口中输入的密码等。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        'logfile'-&gt;ch_logfile('w')</code>
<code class="example"></code>
<code class="example"></code>
ch_open(<code class="special">{address}</code> [, <code class="special">{options}</code>])                                <b class="vimtag"> <a name="ch_open()">ch_open()</a> </b>
                打开通道到 <code class="special">{address}</code>。见  <a href="channel.html#channel">channel</a> 。
                返回通道。错误检查可用  <a href="channel.html#ch_status()">ch_status()</a> 。

                <code class="special">{address}</code> 为字符串，接受的形式可见  <a href="channel.html#channel-address">channel-address</a> 。

                使用 IPv6 地址时，用方括号包围。如 "[2001:db8::1]:8765"。

                <code class="special">{options}</code> 如果给出，必须是  <a href="eval.html#Dictionary">Dictionary</a> 。
                见  <a href="channel.html#channel-open-options">channel-open-options</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetAddress()-&gt;ch_open()</code>
<code class="example"></code>
<code class="example"></code>
ch_read(<code class="special">{handle}</code> [, <code class="special">{options}</code>])                                 <b class="vimtag"> <a name="ch_read()">ch_read()</a> </b>
                从 <code class="special">{handle}</code> 读取，返回收到的消息。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                NL 通道会等待 NL 的到来，除非没有内容可读了 (通道已被关闭)。
                见  <a href="channel.html#channel-more">channel-more</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_read()</code>
<code class="example"></code>
<code class="example"></code>
ch_readblob(<code class="special">{handle}</code> [, <code class="special">{options}</code>])                     <b class="vimtag"> <a name="ch_readblob()">ch_readblob()</a> </b>
                和 ch_read() 类似，但读取二进制数据并返回  <a href="eval.html#Blob">Blob</a> 。
                见  <a href="channel.html#channel-more">channel-more</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_readblob()</code>
<code class="example"></code>
<code class="example"></code>
ch_readraw(<code class="special">{handle}</code> [, <code class="special">{options}</code>])                      <b class="vimtag"> <a name="ch_readraw()">ch_readraw()</a> </b>
                类似于 ch_read()，但 JS 和 JSON 通道不对消息解码。NL 通道不阻
                塞等待 NL 的到来，其余和 ch_read() 类似。
                见  <a href="channel.html#channel-more">channel-more</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_readraw()</code>
<code class="example"></code>
<code class="example"></code>
ch_sendexpr(<code class="special">{handle}</code>, <code class="special">{expr}</code> [, <code class="special">{options}</code>])                     <b class="vimtag"> <a name="ch_sendexpr()">ch_sendexpr()</a> </b>
                发送 <code class="special">{expr}</code> 到 <code class="special">{handle}</code> 上。<code class="special">{expr}</code> 用通道类型进行编码。不能于
                原始通道。见  <a href="channel.html#channel-use">channel-use</a> 。                    <b class="vimtag"> <a name="E912">E912</a> </b>
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                使用 "lsp" 通道模式时，<code class="special">{expr}</code> 必须是  <a href="eval.html#Dict">Dict</a> 。

                通道模式是 "lsp" 时，返回字典。否则返回空串。如果 <code class="special">{options}</code> 里
                有 "callback" 项目，返回的字典包含请求消息的 ID。此 ID 可用来
                给 LSP 服务器发送撤销请求 (如有需要)。出错时返回空字典。

                如果不指望 <code class="special">{expr}</code> 收到响应消息，不要指定 <code class="special">{options}</code> 的
                "callback" 项目。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_sendexpr(expr)</code>
<code class="example"></code>
<code class="example"></code>
ch_sendraw(<code class="special">{handle}</code>, <code class="special">{expr}</code> [, <code class="special">{options}</code>])              <b class="vimtag"> <a name="ch_sendraw()">ch_sendraw()</a> </b>
                发送  <a href="eval.html#String">String</a>  或  <a href="eval.html#Blob">Blob</a>  <code class="special">{expr}</code> 到 <code class="special">{handle}</code> 上。
                类似于  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a> ，但不对请求编码也不对响应结果解码。调用
                者负责内容的正确性。也不对 NL 模式的通道附加换行符，调用者须自
                行负责。响应中的 NL 则被清除。
                见  <a href="channel.html#channel-use">channel-use</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_sendraw(rawexpr)</code>
<code class="example"></code>
<code class="example"></code>
ch_setoptions(<code class="special">{handle}</code>, <code class="special">{options}</code>)                      <b class="vimtag"> <a name="ch_setoptions()">ch_setoptions()</a> </b>
                设置 <code class="special">{handle}</code> 的选项:
                        "callback"      通道回调函数
                        "timeout"       缺省读操作以毫秒计的超时
                        "mode"          整个通道所用的模式
                更多的解释可见  <a href="channel.html#ch_open()">ch_open()</a> 。
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。

                <code class="note">注意</code> 改变模式可能会丢失正在排队的消息。

                以下选项不可改变:
                        "waittime"      只适用于  <a href="channel.html#ch_open()">ch_open()</a> 

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_setoptions(options)</code>
<code class="example"></code>
<code class="example"></code>
ch_status(<code class="special">{handle}</code> [, <code class="special">{options}</code>])                               <b class="vimtag"> <a name="ch_status()">ch_status()</a> </b>
                返回 <code class="special">{handle}</code> 的状态:
                        "fail"          打开通道失败
                        "open"          通道可用
                        "buffered"      通道可读，不可写
                        "closed"        通道不可用
                <code class="special">{handle}</code> 可以是通道或带有通道的作业。
                "buffered" 用于通道已关闭但还有数据用  <a href="channel.html#ch_read()">ch_read()</a>  可读的情况。

                <code class="special">{options}</code> 给出时，可以包含 "part" 项目，指定通道要返回状态的部
                分: "out" 还是 "err"。例如，要得到错误输出的状态: 
<code class="example">                        ch_status(job, {"part": "err"})</code>

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetChannel()-&gt;ch_status()</code>
<code class="example"></code>
</section><hr class="doubleline" /><section class=inner>
<h4>9. 带通道启动作业                                       <b class="vimtag"> <a name="job-start">job-start</a> </b> <b class="vimtag"> <a name="job">job</a> </b></h4>
要启动一个作业并打开对应 stdin/stdout/stderr 的通道: 
<code class="example">    let job = job_start(command, {options})</code>
<code class="example"></code>
可以这样取得对应的通道: 
<code class="example">    let channel = job_getchannel(job)</code>
<code class="example"></code>
此处，通道使用 NL 模式。如果想用其它模式，最好在 <code class="special">{options}</code> 里指定。如果之后再
改变模式，部分文本可能已经被接收而没有得到正确地处理。

如果你要处理命令生成的输出行，给出 stdout 的处理函数: 
<code class="example">    let job = job_start(command, {"out_cb": "MyHandler"})</code>
该函数调用时会被提供通道和一条信息。可以这样定义该函数: 
<code class="example">    func MyHandler(channel, msg)</code>
<code class="example"></code>
如果没有处理函数，你需要通过  <a href="channel.html#ch_read()">ch_read()</a>  或  <a href="channel.html#ch_readraw()">ch_readraw()</a>  读取输出。这一步可
以在关闭回调函数进行。见  <a href="channel.html#read-in-close-cb">read-in-close-cb</a> 。

<code class="note">注意</code> 如果作业在你读取输出结果之前结束，输出可能会丢失。这取决于系统 (Unix 上是
如此，因为管道的写入端的关闭使得读取端读到 EOF)。要避免之，作业退出之前可进行
短暂地休眠。

"out_cb" 定义的处理函数不会读 stderr。如果另外要处理错误，加入 "err_cb" 处理函
数: 
<code class="example">    let job = job_start(command, {"out_cb": "MyHandler",</code>
<code class="example">            \                     "err_cb": "ErrHandler"})</code>
<code class="example"></code>
如果要用相同的处理函数同时处理 stderr 和 stdout，使用 "callback" 选项: 
<code class="example">    let job = job_start(command, {"callback": "MyHandler"})</code>
<code class="example"></code>
取决于系统，启动新的作业可能把 Vim 放到后台，使启动的作业获得焦点。要避免之，
可使用  <a href="builtin.html#foreground()">foreground()</a>  函数。但如果调用太早，可能无效，可在回调处理函数中使用，
或使用定时器使之推迟到作业启动后。

可以用 ch_evalraw() 给命令发送信息。如果通道使用 JSON 或 JS 模式，可用
ch_evalexpr()。

可用选项见  <a href="channel.html#job-options">job-options</a> 。
例如，要启动作业并把其输出写入缓冲区 "dummy": 
<code class="example">        let logjob = job_start("tail -f /tmp/log",</code>
<code class="example">                             \ {'out_io': 'buffer', 'out_name': 'dummy'})</code>
<code class="example">        sbuf dummy</code>
<code class="example"></code>
<code class="example"></code>
<code class="section">作业从缓冲区获得输入 </code>
                                                        <b class="vimtag"> <a name="in_io-buffer">in_io-buffer</a> </b>
要运行的作业从缓冲区中读取内容: 
<code class="example">        let job = job_start({command},</code>
<code class="example">            \ {'in_io': 'buffer', 'in_name': 'mybuffer'})</code>

                                                        <b class="vimtag"> <a name="E915">E915</a> </b> <b class="vimtag"> <a name="E918">E918</a> </b>
缓冲区是通过名字来查找的，类似于  <a href="builtin.html#bufnr()">bufnr()</a> 。该缓冲区必须存在并在 job_start()
调用时己载入。

缺省读入整个缓冲区。可通过 "in_top" 和 "in_bot" 选项改变。

一个特殊的模式是 "in_top" 设为零而 "in_bot" 不设置: 每次缓冲区加入一行新行时，
倒数第二行被发送到作业的 stdin。这样你可以编辑末行并在回车时将它发送。
                                                        <b class="vimtag"> <a name="channel-close-in">channel-close-in</a> </b>
如果不使用这种特殊模式，管道或套接字会在最后一行写入后关闭。这样就告诉了读入端
输入己经完成。也可用  <a href="channel.html#ch_close_in()">ch_close_in()</a>  在更早之前关闭。

文本中的 NUL 字节会被传给作业 (Vim 内部用 NL 字节保存)。


<code class="section">在关闭回调中读取作业输出 </code>
                                                        <b class="vimtag"> <a name="read-in-close-cb">read-in-close-cb</a> </b>
如果作业运行需时而你不需要中间结果，可以加入关闭回调函数，在那里读取输出: 
<code class="example"></code>
<code class="example">        func! CloseHandler(channel)</code>
<code class="example">          while ch_status(a:channel, {'part': 'out'}) == 'buffered'</code>
<code class="example">            echomsg ch_read(a:channel)</code>
<code class="example">          endwhile</code>
<code class="example">        endfunc</code>
<code class="example">        let job = job_start(command, {'close_cb': 'CloseHandler'})</code>
<code class="example"></code>
你要做的事情大概会比 "echomsg" 有用些吧。

</section><hr class="doubleline" /><section class=inner>
<h4>10. 不带通道启动作业                                    <b class="vimtag"> <a name="job-start-nochannel">job-start-nochannel</a> </b></h4>
要启动别处的进程而不建立通道: 
<code class="example">    let job = job_start(command,</code>
<code class="example">        \ {"in_io": "null", "out_io": "null", "err_io": "null"})</code>
<code class="example"></code>
<code class="special">{command}</code> 会在后台启动，Vim 不等待其运行结束。

如果 Vim 看到 stdin、stdout 或 stderr 没有一个被连接，则不会建立通道。一般，你
需要进行重定向以保证命令不会被卡住。

可用选项见  <a href="channel.html#job-options">job-options</a> 。

                                                        <b class="vimtag"> <a name="job-start-if-needed">job-start-if-needed</a> </b>
要想在不能连接某个地址的时候才启动某个作业，可以这么做: 
<code class="example">        let channel = ch_open(address, {"waittime": 0})</code>
<code class="example">        if ch_status(channel) == "fail"</code>
<code class="example">          let job = job_start(command)</code>
<code class="example">          let channel = ch_open(address, {"waittime": 1000})</code>
<code class="example">        endif</code>
<code class="example"></code>
<code class="note">注意</code> 这里 ch_open() 的 waittime 给作业一秒钟时间使端口可用。

</section><hr class="doubleline" /><section class=inner>
<h4>11. 作业函数                                            <b class="vimtag"> <a name="job-functions-details">job-functions-details</a> </b></h4>
job_getchannel(<code class="special">{job}</code>)                                    <b class="vimtag"> <a name="job_getchannel()">job_getchannel()</a> </b>
                获取 <code class="special">{job}</code> 使用的通道句柄。
                要确认作业没有通道: 
<code class="example">                        if string(job_getchannel(job)) == 'channel fail'</code>

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetJob()-&gt;job_getchannel()</code>
<code class="example"></code>
job_info([<code class="special">{job}</code>])                                       <b class="vimtag"> <a name="job_info()">job_info()</a> </b>
                返回关于 <code class="special">{job}</code> 信息的字典:
                   "status"      <a href="channel.html#job_status()">job_status()</a>  返回值
                   "channel"     <a href="channel.html#job_getchannel()">job_getchannel()</a>  返回值
                   "cmd"        启动作业的命令行参数列表
                   "process"    进程 ID
                   "tty_in"     终端输入名，如果没有则为空
                   "tty_out"    终端输出名，如果没有则为空
                   "exitval"    "status" 为 "dead" 时才有效
                   "exit_cb"    退出时调用的函数
                   "stoponexit"  <a href="channel.html#job-stoponexit">job-stoponexit</a> 

                   仅用于 Unix:
                   "termsig"    终止程序的信号 (相关值见  <a href="channel.html#job_stop()">job_stop()</a> )
                                仅当 "status" 为 "dead" 时才有意义

                   仅用于 MS-Windows:
                   "tty_type"   使用的虚拟控制台类型。
                                可选值是 "winpty" 或 "conpty"。
                                见 <a href="options.html#'termwintype'">'termwintype'</a>。

                无参数时返回所有作业对象的列表。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetJob()-&gt;job_info()</code>
<code class="example"></code>
<code class="example"></code>
job_setoptions(<code class="special">{job}</code>, <code class="special">{options}</code>)                        <b class="vimtag"> <a name="job_setoptions()">job_setoptions()</a> </b>
                改变 <code class="special">{job}</code> 的选项。目前支持:
                   "stoponexit"  <a href="channel.html#job-stoponexit">job-stoponexit</a> 
                   "exit_cb"     <a href="channel.html#job-exit_cb">job-exit_cb</a> 

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetJob()-&gt;job_setoptions(options)</code>
<code class="example"></code>
<code class="example"></code>
job_start(<code class="special">{command}</code> [, <code class="special">{options}</code>])                      <b class="vimtag"> <a name="job_start()">job_start()</a> </b>
                启动作业，返回 Job 对象。不同于  <a href="builtin.html#system()">system()</a>  和  <a href="various.html#:!cmd">:!cmd</a> ，不等待
                作业完成。
                要在终端窗口中启动作业，可见  <a href="terminal.html#term_start()">term_start()</a> 。

                如果作业启动失败，在返回的 Job 对象上调用  <a href="channel.html#job_status()">job_status()</a>  会报
                告 "fail"，而且不会调用任何回调。

                <code class="special">{command}</code> 可以是字符串。对 MS-Windows 最佳。Unix 上先切为空白
                分隔的部分，然后传给 execvp()。而双引号内的参数可能包含空格。

                <code class="special">{command}</code> 可以是列表，首项是可执行文件，后面诸项为参数。所有项
                目转换为字符串。这对 Unix 最佳。

                MS-Windows 上，job_start() 启动隐藏的 GUI 应用。要使之可见，用
                 <a href="os_win32.html#:!start">:!start</a>  代替。

                命令直接执行，不通过外壳，不使用 <a href="options.html#'shell'">'shell'</a> 选项。要使用外壳: 
<code class="example">        let job = job_start(["/bin/sh", "-c", "echo hello"])</code>
                或: 
<code class="example">        let job = job_start('/bin/sh -c "echo hello"')</code>
                <code class="note">注意</code> 这样会启动两个过程，外壳及其启动的命令。如果不希望如此，
                可用 "exec" 外壳命令。

                Unix 上仅当命令本身不包含斜杠时，才用 $PATH 来搜索可执行文件。

                作业使用和 Vim 相同的终端。如果从 stdin 读入，该作业和 Vim 会
                竞争输入，这样不行。要避免此问题，重定向 stdin 和 stdout: 
<code class="example">        let job = job_start(['sh', '-c', "myserver &lt;/dev/null &gt;/dev/null"])</code>

                返回的 Job 对象可用以通过  <a href="channel.html#job_status()">job_status()</a>  获取状态，和通过
                 <a href="channel.html#job_stop()">job_stop()</a>  停止作业。

                <code class="note">注意</code> 作业对象如果没有被引用会被删除。此时 stdin 和 stderr 会被
                关闭，这可能会使作业失败报错。要避免之，总是保留对作业的引用。
                所以，不要这样: 
<code class="example">        call job_start('my-command')</code>
                而要: 
<code class="example">        let myjob = job_start('my-command')</code>
                不再需要作业，或者已经不介意作业是否会失败之后 (如启动时已显示
                了信息) 才可以 unlet "myjob"。记住函数返回后局部于函数的变量不
                复存在。如有需要，可用局部于脚本的变量: 
<code class="example">        let s:myjob = job_start('my-command')</code>

                <code class="special">{options}</code> 必须是字典。可包含许多可选项目，见  <a href="channel.html#job-options">job-options</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        BuildCommand()-&gt;job_start()</code>
<code class="example"></code>
<code class="example"></code>
job_status(<code class="special">{job}</code>)                                       <b class="vimtag"> <a name="job_status()">job_status()</a> </b> <b class="vimtag"> <a name="E916">E916</a> </b>
                返回字符串，返回 <code class="special">{job}</code> 的状态:
                        "run"   作业运行中
                        "fail"  作业无法启动
                        "dead"  作业启动后结束或被终止

                Unix 上不存在的命令会报告 "dead" 而不是 "fail"，因为在检测到问
                题之前，fork 操作已经发生。

                Vim9 脚本中如果有变量声明为 "job" 类型但从未赋值，传递该变量到
                job_status() 会返回 "fail"。

                如果用 "exit_cb" 选项设置了退出回调，而作业已检测到处于 "dead"
                状态，调用该回调。

                更多详情可见  <a href="channel.html#job_info()">job_info()</a> 。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetJob()-&gt;job_status()</code>
<code class="example"></code>
<code class="example"></code>
job_stop(<code class="special">{job}</code> [, <code class="special">{how}</code>])                                       <b class="vimtag"> <a name="job_stop()">job_stop()</a> </b>
                停止 <code class="special">{job}</code>。也用于给作业发送信号。

                如果 <code class="special">{how}</code> 省略或是 "term"，作业被终止。Unix 上发送 SIGTERM。
                MS-Windows 上强制终止该作业 (没有所谓 "温柔的" 方式)。
                应用在整个进程组上，因而子进程也受影响。

                Unix 上的效果:
                        "term"   SIGTERM (缺省)
                        "hup"    SIGHUP
                        "quit"   SIGQUIT
                        "int"    SIGINT
                        "kill"   SIGKILL (最强的停止方式)
                        数值     该数值对应的信号

                MS-Windows 上的效果:
                        "term"   强制终止进程 (缺省)
                        "hup"    CTRL_BREAK
                        "quit"   CTRL_BREAK
                        "int"    CTRL_C
                        "kill"   强制终止进程
                        Others   CTRL_BREAK

                Unix 上发送信号到进程组。这意味着如果作业是 "sh -c command"，
                外壳和命令同时受影响。

                返回数值: 如果操作可执行返回 1，如果系统不支持 "how"，返回 0。
                <code class="note">注意</code> 即使已执行了操作，作业停止与否需用  <a href="channel.html#job_status()">job_status()</a>  检查。

                如果作业的状态是 "dead"，不发送信号。这是为了避免给错误的作业
                发送信号 (尤其在 Unix 上，进程号是循环利用的)。

                使用 "kill" 后，Vim 假定作业会终止，因而将通道关闭。

                也可用作  <a href="eval.html#method">method</a> : 
<code class="example">                        GetJob()-&gt;job_stop()</code>
<code class="example"></code>
<code class="example"></code>
</section><hr class="doubleline" /><section class=inner>
<h4>12. 作业选项                                            <b class="vimtag"> <a name="job-options">job-options</a> </b></h4>
job_start() 的 <code class="special">{options}</code> 参数是一个字典。所有的项目都是可选的。有些选项可以在
作业启动后通过 job_setoptions(job, <code class="special">{options}</code>) 给出。许多选项可通过
ch_setoptions(channel, <code class="special">{options}</code>) 对作业相关的通道给出。
见  <a href="channel.html#job_setoptions()">job_setoptions()</a>  和  <a href="channel.html#ch_setoptions()">ch_setoptions()</a> 。

                                                <b class="vimtag"> <a name="in_mode">in_mode</a> </b> <b class="vimtag"> <a name="out_mode">out_mode</a> </b> <b class="vimtag"> <a name="err_mode">err_mode</a> </b>
"in_mode"               stdin 特定的模式，仅适用于管道情形
"out_mode"              stdout 特定的模式，仅适用于管道情形
"err_mode"              stderr 特定的模式，仅适用于管道情形
                        可用值参见  <a href="channel.html#channel-mode">channel-mode</a> 。

                        <code class="note">注意</code>: 设置 "mode" 时，覆盖部分特定的模式。因而，先设置
                        "mode"，再设置部分特定的模式。

                        <code class="note">备注</code>: 写入文件或缓冲区和从缓冲区读取时，缺省使用 NL 模
                        式。

                                                <b class="vimtag"> <a name="job-noblock">job-noblock</a> </b>
"noblock": 1            写入时使用非阻塞式的写调用。避免 Vim 在处理其它信息之
                        间，比如作业把数据发还给 Vim 的时候，长时间等待，这意
                        味着  <a href="channel.html#ch_sendraw()">ch_sendraw()</a>  返回时未必所有的数据已经写回。
                        8.1.0350 补丁加入此选项，可如此测试之: 
<code class="example">                                if has("patch-8.1.350")</code>
<code class="example">                                  let options['noblock'] = 1</code>
<code class="example">                                endif</code>

                                                <b class="vimtag"> <a name="job-callback">job-callback</a> </b>
"callback": handler     通道任何部分有读取内容时的回调。
                                                <b class="vimtag"> <a name="job-out_cb">job-out_cb</a> </b> <b class="vimtag"> <a name="out_cb">out_cb</a> </b>
"out_cb": handler       stdout 有读取内容时的回调。仅适用于通道使用管道时。如
                        果不设 "out_cb"，使用通道本身的回调。
                        两个参数是通道和信息。

                                                <b class="vimtag"> <a name="job-err_cb">job-err_cb</a> </b> <b class="vimtag"> <a name="err_cb">err_cb</a> </b>
"err_cb": handler       stderr 有读取内容时的回调。仅适用于通道使用管道时。如
                        果不设 "err_cb"，使用通道本身的回调。
                        两个参数是通道和信息。
                                                <b class="vimtag"> <a name="job-close_cb">job-close_cb</a> </b>
"close_cb": handler     通道关闭时的回调。与  <a href="channel.html#ch_open()">ch_open()</a>  的 "close_cb" 相同，
                        见  <a href="channel.html#close_cb">close_cb</a> 。
                                                <b class="vimtag"> <a name="job-drop">job-drop</a> </b>
"drop": when            指定何时丢弃消息。与  <a href="channel.html#ch_open()">ch_open()</a>  的 "drop" 相同，见
                         <a href="channel.html#channel-drop">channel-drop</a> 。如用 "auto"，不考虑 exit_cb。
                                                <b class="vimtag"> <a name="job-exit_cb">job-exit_cb</a> </b>
"exit_cb": handler      作业结束时的回调。参数是作业和退出状态。
                        对退出的作业，Vim 作最多每秒十次的检查。也可以通过
                         <a href="channel.html#job_status()">job_status()</a>  调用触发检查，这样也会调用 exit_cb 处理
                        函数。
                        <code class="note">备注</code> 数据可能会缓冲，进程结束后还可能调用回调。
                                                        <b class="vimtag"> <a name="job-timeout">job-timeout</a> </b>
"timeout": time         等待阻塞请求，如 ch_evalexpr() 等的时间，以毫秒计。缺
                        省是 2000 (2 秒)。
                                                <b class="vimtag"> <a name="out_timeout">out_timeout</a> </b> <b class="vimtag"> <a name="err_timeout">err_timeout</a> </b>
"out_timeout": time     stdout 所用的超时。仅适用于管道情形。
"err_timeout": time     stderr 所用的超时。仅适用于管道情形。
                        <code class="note">注意</code>: 设置 "timeout" 时，覆盖部分特定的模式。因而，先
                        设置 "timeout"，再设置部分特定的模式。

                                                <b class="vimtag"> <a name="job-stoponexit">job-stoponexit</a> </b>
"stoponexit": <code class="special">{signal}</code>  Vim 结束时给作业发信号 <code class="special">{signal}</code>。可取值参见
                         <a href="channel.html#job_stop()">job_stop()</a> 。
"stoponexit": ""        Vim 结束时不结束作业。
                        缺省是 "term"。

                                                <b class="vimtag"> <a name="job-term">job-term</a> </b>
"term": "open"          在新窗口中启动终端并重定向作业的 stdin/stdout/stderr
                        到终端。和  <a href="terminal.html#:terminal">:terminal</a>  类似。
                        <code class="note">注意</code>: 尚未实现！

"channel": <code class="special">{channel}</code>    使用己有的通道，而不重新创建。
                        新作业所用的通道部分会从之前的使用者处断开。如果通道还
                        在被其它的作业使用，这可能会导致 I/O 错误。
                        已有的回调和其它设置不变。

"pty": 1                如有可能，使用 pty (伪终端) 而非管道。最常和终端窗口组
                        合使用，见  <a href="terminal.html#terminal">terminal</a> 。
                        <code class="notvi">{仅在 Unix 和类似系统中才有效}</code>

                                <b class="vimtag"> <a name="job-in_io">job-in_io</a> </b> <b class="vimtag"> <a name="in_top">in_top</a> </b> <b class="vimtag"> <a name="in_bot">in_bot</a> </b> <b class="vimtag"> <a name="in_name">in_name</a> </b> <b class="vimtag"> <a name="in_buf">in_buf</a> </b>
"in_io": "null"         不连接 stdin (从 /dev/null 读取)
"in_io": "pipe"         连接 stdin 到通道 (缺省)
"in_io": "file"         stdin 从文件读取
"in_io": "buffer"       stdin 从缓冲区读取
"in_top": number        用 "buffer" 时: 发送的首行 (缺省: 1)
"in_bot": number        用 "buffer" 时: 发送的末行 (缺省: 最后一行)
"in_name": "/path/file" 读取的文件或缓冲区名
"in_buf": number        读取的缓冲区号

                                <b class="vimtag"> <a name="job-out_io">job-out_io</a> </b> <b class="vimtag"> <a name="out_name">out_name</a> </b> <b class="vimtag"> <a name="out_buf">out_buf</a> </b>
"out_io": "null"        不连接 stdout (写到 /dev/null)
"out_io": "pipe"        连接 stdout 到通道 (缺省)
"out_io": "file"        stdout 写入文件
"out_io": "buffer"      stdout 附加到缓冲区 (见下)
"out_name": "/path/file" 写入的文件或缓冲区名
"out_buf": number       写入的缓冲区号
"out_modifiable": 0     写入到缓冲区时，关闭 <a href="options.html#'modifiable'">'modifiable'</a> (见下)
"out_msg": 0            写入新缓冲区时，将首行置为 "Reading from channel
                        error..."

                                <b class="vimtag"> <a name="job-err_io">job-err_io</a> </b> <b class="vimtag"> <a name="err_name">err_name</a> </b> <b class="vimtag"> <a name="err_buf">err_buf</a> </b>
"err_io": "out"         stderr 信息写到 stdout
"err_io": "null"        不连接 stderr  (写到 /dev/null)
"err_io": "pipe"        连接 stderr 到通道 (缺省)
"err_io": "file"        stderr 写入文件
"err_io": "buffer"      stderr 附加到缓冲区 (见下)
"err_name": "/path/file" 写入的文件或缓冲区名
"err_buf": number       写入的缓冲区号
"err_modifiable": 0     写入到缓冲区时，关闭 <a href="options.html#'modifiable'">'modifiable'</a> (见下)
"err_msg": 0            写入新缓冲区时，将首行置为 "Reading from channel
                        error..."

"block_write": number   只用于测试: 模拟每隔一个的 stdin 写入是阻塞写入

"env": dict             新进程使用的环境变量
"cwd": "/path/to/dir"   新进程的当前工作目录；如该目录不存在则报错


<code class="section">写入缓冲区 </code>
                                                        <b class="vimtag"> <a name="out_io-buffer">out_io-buffer</a> </b>
如果 out_io 或 err_io 模式是 "buffer"，而且回调存在，先把文本附加至缓冲区尾，
然后再调用回调。

如果缓冲区既用于输入也用于输出，输出行放置在尾行之前，因为尾行是用于写到通道输
入的。否则，输出行放置在尾行之后。

如果 "buffer" 使用 JS 或 JSON 模式，只有 ID 为零或负数的消息被经过解码和编码后
加入缓冲区。带正数的消息则被回调处理。命令的处理则如常。

"out_name" 或 "err_name" 用的缓冲区名字和已有的缓冲区的完整名字相比较，当前目
录也被扩展。例如，如果以 ":edit somename" 创建了缓冲区，而缓冲区名是
"somename"，则使用该缓冲区。

如果没有匹配的缓冲区，则创建新的缓冲区。如果使用空名，则一定会创建新缓冲区。
 <a href="channel.html#ch_getbufnr()">ch_getbufnr()</a>  可用来得到缓冲区编号。

新缓冲区的 <a href="options.html#'buftype'">'buftype'</a> 设为 "nofile"，而 <a href="options.html#'bufhidden'">'bufhidden'</a> 为 "hide"。要想用别的设置，
先建立好缓冲区，然后传入其缓冲区号。
                                        <b class="vimtag"> <a name="out_modifiable">out_modifiable</a> </b> <b class="vimtag"> <a name="err_modifiable">err_modifiable</a> </b>
"out_modifiable" 和 "err_modifiable" 选项可用来关闭 <a href="options.html#'modifiable'">'modifiable'</a> 选项，或写入
关闭了 <a href="options.html#'modifiable'">'modifiable'</a> 的缓冲区。这意味着新行被附加到缓冲区后，但用户却不能轻易地
修改此缓冲区。
                                        <b class="vimtag"> <a name="out_msg">out_msg</a> </b> <b class="vimtag"> <a name="err_msg">err_msg</a> </b>
"out_msg" 选项可用来指定新缓冲区是否将首行设为 "Reading from channel
output..."。缺省是加入该信息。"err_msg" 则对错误通道同样办理。

如果要写入已有的缓冲区，但其 <a href="options.html#'modifiable'">'modifiable'</a> 已关闭并且 "out_modifiable" 或
"err_modifiable" 选项非零，报错并拒绝写入訪缓冲区。

如果写入的缓冲区在窗口显示并且光标在末行的首列，光标会被移动到新行上，有必要的
话滚动窗口以显示光标所在的位置。

所有新加的行都同步了撤销历史。接受 NUL 字节 (Vim 内部存为 NL 字节)。


<code class="section">写入文件 </code>
                                                        <b class="vimtag"> <a name="E920">E920</a> </b>
文件建立的缺省权限为 600 (用户可读写，其它人不能访问)。可用  <a href="builtin.html#setfperm()">setfperm()</a>  改
变。

如果文件已存在，文件内容被清空。

</section><hr class="doubleline" /><section class=inner>
<h4>13. 控制作业                                            <b class="vimtag"> <a name="job-control">job-control</a> </b></h4>
要获知作业的状态: 
<code class="example">        echo job_status(job)</code>
<code class="example"></code>
要停止作业的运行: 
<code class="example">        job_stop(job)</code>
<code class="example"></code>
这是停止作业的正常方式，Unix 上会给作业发出 SIGTERM。也可以用其它方式来停止作
业，甚至可以发送任意的信号。例如，要强制终止作业，"杀了它": 
<code class="example">        job_stop(job, "kill")</code>
<code class="example"></code>
更多选项，可见  <a href="channel.html#job_stop()">job_stop()</a> 。

</section><hr class="doubleline" /><section class=inner>
<h4>14. 使用提示缓冲区                                      <b class="vimtag"> <a name="prompt-buffer">prompt-buffer</a> </b></h4>
如果你希望在 Vim 窗口中为作业提供输入，有几种选项:
- 使用普通缓冲区，自行处理所有可能的命令。这很复杂，因为可能的命令有很多。
- 使用终端窗口。此方式直接在窗口中键入作业输入并显示作业的输出。
  见  <a href="terminal.html#terminal-window">terminal-window</a> 。
- 使用带提示缓冲区的窗口。此方式下在 Vim 中输入一行作业命令，而在窗口中显示作
  业的 (可能经过过滤的) 输出。

通过设置 <a href="options.html#'buftype'">'buftype'</a> 为 "prompt" 创立提示缓冲区。通常只在缓冲区新建时使用。

用户可以在缓冲区的末行上编辑和输入一行文本行。在该提示行上输入 Enter 后，激活
 <a href="builtin.html#prompt_setcallback()">prompt_setcallback()</a>  设置的回调。通常，它会把该行发送给作业。另一个回调会从
作业接收输出，并在缓冲区中显示结果。显示位置在提示文本之下 (但在下一个提示文本
的上方)。

只有末行提示文本之后的文本可进行编辑。普通模式命令不可修改缓冲区其余部分。但通
过调用如  <a href="builtin.html#append()">append()</a>  等的函数，还是可以的。使用其它命令可能搞乱缓冲区。

设置 <a href="options.html#'buftype'">'buftype'</a> 为 "prompt" 时，Vim 并不自动启动插入模式。如想开始插入模式，用
 <a href="insert.html#:startinsert">:startinsert</a> ，这样用户可以开始行输入。

通过  <a href="builtin.html#prompt_setprompt()">prompt_setprompt()</a>  函数可设置提示文本。如果没有用
 <a href="builtin.html#prompt_setprompt()">prompt_setprompt()</a> ，缺省用 "% "。要获取缓冲区的实际提示文本，可用
 <a href="builtin.html#prompt_getprompt()">prompt_getprompt()</a> 。

用户可用普通模式浏览缓冲区，以便查看旧作业的输出或进行文本复制。

<code class="keystroke">CTRL-W</code> 键可用于开始窗口命令，例如，<code class="keystroke">CTRL-W</code> w 可切换到下一个窗口。这也适用于插入
模式 (用 Shift-<code class="keystroke">CTRL-W</code> 删除单词)。离开提示窗口时会停止插入模式。而回到提示窗口
时会复原窗口模式。

任何启动插入模式的命令，如 "a"、"i"、"A" 和 "I" 等，会自动移动光标到末行。"A"
会移到行尾，而 "I" 会移到行首。

以下是用于 Unix 的一个例子。它在后台启动外壳，给出提示行以键入下个外壳命令。而
外壳的输出会在提示行之上显示。 
<code class="example"></code>
<code class="example">        " 建立通道日志，以记录发生的事件。</code>
<code class="example">        call ch_logfile('logfile', 'w')</code>
<code class="example"></code>
<code class="example">        " 处理键入文本行的函数。</code>
<code class="example">        func TextEntered(text)</code>
<code class="example">          " 给文本附加 Enter，发送到外壳。</code>
<code class="example">          call ch_sendraw(g:shell_job, a:text .. "\n")</code>
<code class="example">        endfunc</code>
<code class="example"></code>
<code class="example">        " 处理外壳输出的函数: 附加到提示行之上。</code>
<code class="example">        func GotOutput(channel, msg)</code>
<code class="example">          call append(line("$") - 1, "- " .. a:msg)</code>
<code class="example">        endfunc</code>
<code class="example"></code>
<code class="example">        " 处理外壳退出的函数: 关闭窗口。</code>
<code class="example">        func JobExit(job, status)</code>
<code class="example">          quit!</code>
<code class="example">        endfunc</code>
<code class="example"></code>
<code class="example">        " 在后台启动外壳。</code>
<code class="example">        let shell_job = job_start(["/bin/sh"], #{</code>
<code class="example">                \ out_cb: function('GotOutput'),</code>
<code class="example">                \ err_cb: function('GotOutput'),</code>
<code class="example">                \ exit_cb: function('JobExit'),</code>
<code class="example">                \ })</code>
<code class="example"></code>
<code class="example">        new</code>
<code class="example">        set buftype=prompt</code>
<code class="example">        let buf = bufnr('')</code>
<code class="example">        call prompt_setcallback(buf, function("TextEntered"))</code>
<code class="example">        eval prompt_setprompt(buf, "shell command: ")</code>
<code class="example"></code>
<code class="example">        " 开始接受外壳命令</code>
<code class="example">        startinsert</code>

同一例子，使用  <a href="vim9.html#Vim9">Vim9</a>  脚本: 
<code class="example"></code>
<code class="example">        vim9script</code>
<code class="example"></code>
<code class="example">        # 建立通道日志，以记录发生的事件。</code>
<code class="example">        ch_logfile('logfile', 'w')</code>
<code class="example"></code>
<code class="example">        var shell_job: job</code>
<code class="example"></code>
<code class="example">        # 处理键入文本行的函数。</code>
<code class="example">        def TextEntered(text: string)</code>
<code class="example">          # 给文本附加 Enter，发送到外壳。</code>
<code class="example">          ch_sendraw(shell_job, text .. "\n")</code>
<code class="example">        enddef</code>
<code class="example"></code>
<code class="example">        # 处理外壳输出的函数: 附加到提示行之上。</code>
<code class="example">        def GotOutput(channel: channel, msg: string)</code>
<code class="example">          append(line("$") - 1, "- " .. msg)</code>
<code class="example">        enddef</code>
<code class="example"></code>
<code class="example">        # 处理外壳退出的函数: 关闭窗口。</code>
<code class="example">        def JobExit(job: job, status: number)</code>
<code class="example">          quit!</code>
<code class="example">        enddef</code>
<code class="example"></code>
<code class="example">        # 在后台启动外壳。</code>
<code class="example">        shell_job = job_start(["/bin/sh"], {</code>
<code class="example">                                 out_cb: GotOutput,</code>
<code class="example">                                 err_cb: GotOutput,</code>
<code class="example">                                 exit_cb: JobExit,</code>
<code class="example">                                 })</code>
<code class="example"></code>
<code class="example">        new</code>
<code class="example">        set buftype=prompt</code>
<code class="example">        var buf = bufnr('')</code>
<code class="example">        prompt_setcallback(buf, TextEntered)</code>
<code class="example">        prompt_setprompt(buf, "shell command: ")</code>
<code class="example"></code>
<code class="example">        # 开始接受外壳命令</code>
<code class="example">        startinsert</code>
<code class="example"></code>
</section><hr class="doubleline" /><section class=inner>
<h4>15. 语言服务器协议                              <b class="vimtag"> <a name="language-server-protocol">language-server-protocol</a> </b></h4>
可在这里获取语言服务器规范:

    <a href="https://microsoft.github.io/language-server-protocol/specification">https://microsoft.github.io/language-server-protocol/specification</a>

每个 LSP 协议消息以简单 HTTP 头开始，后跟 JSON-RPC 格式编码的负载。JSON-RPC 格
式描述可见:

    <a href="https://www.jsonrpc.org/specification">https://www.jsonrpc.org/specification</a>

连接到 LSP 服务器并使用  <a href="channel.html#channel-mode">channel-mode</a>  设为 "lsp"，可以把 Vim  <a href="eval.html#Dict">Dict</a>  形式的
LSP 请求/通告消息编码为 LSP JSON-RPC 消息并发送至服务器，然后接收 LSP JSON-RPC
形式的响应/通告消息并解码为 Vim  <a href="eval.html#Dict">Dict</a> 。

 <a href="channel.html#channel-mode">channel-mode</a>  设为 "lsp" 时，对通道接收到的消息，Vim 会处理 HTTP 头部，并把
JSON-RPC 负载解码为 Vim  <a href="eval.html#Dict">Dict</a>  类型，并调用  <a href="channel.html#channel-callback">channel-callback</a>  函数或指定的
 <a href="channel.html#channel-onetime-callback">channel-onetime-callback</a>  函数。用  <a href="channel.html#ch_evalexpr()">ch_evalexpr()</a>  或  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  函数在
通道上发送消息时，Vim 会附加 HTTP 头部，并把 Vim 表达式编码为 JSON。
参见  <a href="builtin.html#json_encode()">json_encode()</a>  和  <a href="builtin.html#json_decode()">json_decode()</a>  说明 Vim 编码和解码内建类型到 JSON 的
详情。

要用 <a href="options.html#'lsp'">'lsp'</a> 模式打开通道，把  <a href="channel.html#ch_open()">ch_open()</a>  的 <code class="special">{options}</code> 参数的 <code class="badlink">'mode'</code> 项目设为
<a href="options.html#'lsp'">'lsp'</a>。示例: 
<code class="example"></code>
<code class="example">    let ch = ch_open(..., #{mode: 'lsp'})</code>
<code class="example"></code>
要用 <a href="options.html#'lsp'">'lsp'</a> 模式打开通道并使用作业，把  <a href="channel.html#job_start()">job_start()</a>  的 <code class="special">{options}</code> 参数的
'in_mode' 和 'out_mode' 项目设为 <a href="options.html#'lsp'">'lsp'</a>。示例: 
<code class="example"></code>
<code class="example">    let cmd = ['clangd', '--background-index', '--clang-tidy']</code>
<code class="example">    let opts = {}</code>
<code class="example">    let opts.in_mode = 'lsp'</code>
<code class="example">    let opts.out_mode = 'lsp'</code>
<code class="example">    let opts.err_mode = 'nl'</code>
<code class="example">    let opts.out_cb = function('LspOutCallback')</code>
<code class="example">    let opts.err_cb = function('LspErrCallback')</code>
<code class="example">    let opts.exit_cb = function('LspExitCallback')</code>
<code class="example">    let job = job_start(cmd, opts)</code>
<code class="example"></code>
<code class="note">注意</code> 如果作业使用 stdout 来输出 LSP 消息，而用 stderr 来输出非-LSP 消息，
通道-回调函数就可以正确处理两种消息格式，不然，你可用上面展示的方式来分别指定
"out_cb" 和 "err_cb" 的回调函数。

要同步发送 JSON-RPC 请求到服务器，可用  <a href="channel.html#ch_evalexpr()">ch_evalexpr()</a>  函数。此函数会等待并返
回经解码的服务器的响应消息。可用  <a href="channel.html#channel-timeout">channel-timeout</a>  或 <code class="special">{options}</code> 参数的
<a href="options.html#'timeout'">'timeout'</a> 字段来控制等待响应的时间。如果响应超时，返回空  <a href="eval.html#Dict">Dict</a> 。例如: 
<code class="example"></code>
<code class="example">    let req = {}</code>
<code class="example">    let req.method = 'textDocument/definition'</code>
<code class="example">    let req.params = {}</code>
<code class="example">    let req.params.textDocument = #{uri: 'a.c'}</code>
<code class="example">    let req.params.position = #{line: 10, character: 3}</code>
<code class="example">    let defs = ch_evalexpr(ch, req, #{timeout: 100})</code>
<code class="example">    if defs-&gt;empty()</code>
<code class="example">        ... &lt;handle failure&gt;</code>
<code class="example">    endif</code>
<code class="example"></code>
<code class="note">注意</code> 请求消息不应指定 <code class="badlink">'id'</code> 字段。即使指定，Vim 会用内部生成的标识符取而代之。
Vim 目前只支持数值类型的 <code class="badlink">'id'</code> 字段。不管 RPC 请求是成功还是失败，回调函数都会
被调用。

要发送 JSON-RPC 请求到服务器并异步处理其响应，可用  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  函数并提供
回调函数。如果请求消息指定了 "id" 字段，Vim 会用内部生成的标识符取而代之。此函
数返回字典，其中包含消息所用的标识符。这可用于给 LSP 服务器发送撤销请求 (如有
需要)。例如: 
<code class="example"></code>
<code class="example">    let req = {}</code>
<code class="example">    let req.method = 'textDocument/hover'</code>
<code class="example">    let req.id = 200</code>
<code class="example">    let req.params = {}</code>
<code class="example">    let req.params.textDocument = #{uri: 'a.c'}</code>
<code class="example">    let req.params.position = #{line: 10, character: 3}</code>
<code class="example">    let resp = ch_sendexpr(ch, req, #{callback: 'HoverFunc'})</code>
<code class="example"></code>
要撤销  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  函数发送给服务器的尚未完成的异步 LSP 请求，用发送此请求
的  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  函数返回的 ID 来调用  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  以给服务器发送撤销消
息。例如: 
<code class="example"></code>
<code class="example">    " 发送 completion 请求</code>
<code class="example">    let req = {}</code>
<code class="example">    let req.method = 'textDocument/completion'</code>
<code class="example">    let req.params = {}</code>
<code class="example">    let req.params.textDocument = #{uri: 'a.c'}</code>
<code class="example">    let req.params.position = #{line: 10, character: 3}</code>
<code class="example">    let reqstatus = ch_sendexpr(ch, req, #{callback: 'LspComplete'})</code>
<code class="example">    " 发送撤销通告</code>
<code class="example">    let notif = {}</code>
<code class="example">    let notif.method = '$/cancelRequest'</code>
<code class="example">    let notif.id = reqstatus.id</code>
<code class="example">    call ch_sendexpr(ch, notif)</code>
<code class="example"></code>
要给服务器发送 JSON-RPC 通告消息，用  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  函数。因为服务器不会为通
告发送响应消息，不需要指定 "callback" 项目。例如: 
<code class="example"></code>
<code class="example">    call ch_sendexpr(ch, #{method: 'initialized'})</code>
<code class="example"></code>
要响应服务器发送的 JSON-RPC 请求消息，用  <a href="channel.html#ch_sendexpr()">ch_sendexpr()</a>  函数。在响应消息里，
复制服务器请求消息里的 <code class="badlink">'id'</code> 字段值。例如: 
<code class="example"></code>
<code class="example">    let resp = {}</code>
<code class="example">    let resp.id = req.id</code>
<code class="example">    let resp.result = 1</code>
<code class="example">    call ch_sendexpr(ch, resp)</code>
<code class="example"></code>
来自服务器的 JSON-RPC 通告消息会用  <a href="channel.html#channel-callback">channel-callback</a>  函数递送。

取决于用例，可在同一通道上使用 ch_evalexpr()、ch_sendexpr() 和 ch_sendraw() 函
数。

LSP 请求消息有以下格式 (以 Vim 字典形式表示)。"params" 字段可选: 
<code class="example"></code>
<code class="example">    {</code>
<code class="example">        "jsonrpc": "2.0",</code>
<code class="example">        "id": &lt;number&gt;,</code>
<code class="example">        "method": &lt;string&gt;,</code>
<code class="example">        "params": &lt;list|dict&gt;</code>
<code class="example">    }</code>
<code class="example"></code>
LSP 响应消息有以下格式 (以 Vim 字典形式表示)。"result" 和 "error" 字段可选: 
<code class="example"></code>
<code class="example">    {</code>
<code class="example">        "jsonrpc": "2.0",</code>
<code class="example">        "id": &lt;number&gt;,</code>
<code class="example">        "result": &lt;vim type&gt;</code>
<code class="example">        "error": &lt;dict&gt;</code>
<code class="example">    }</code>
<code class="example"></code>
LSP 通告消息有以下格式 (以 Vim 字典形式表示)。"params" 字段可选: 
<code class="example"></code>
<code class="example">    {</code>
<code class="example">        "jsonrpc": "2.0",</code>
<code class="example">        "method": &lt;string&gt;,</code>
<code class="example">        "params": &lt;list|dict&gt;</code>
<code class="example">    }</code>
<code class="example"></code>

 vim:tw=78:ts=8:noet:ft=help:norl:
</section>
</article>
<footer>
Generated by vim2html
</footer>
</body>
</html>
